package cgl.narada.transport.rtp;
/**
 * Software License, Version 1.0
 *
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 *
 *Redistribution and use in source and binary forms, with or without
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For
 * further information contact the Community Grids Lab at
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and
 * wherever such third-party acknowledgments normally appear.
 *
 *4) The name Indiana University or Community Grids Lab or GlobalMMCS,
 * shall not be used to endorse or promote products derived from this software
 * without prior written permission from Indiana University.  For written
 * permission, please contact the Advanced Research and Technology Institute
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called GlobalMMCS,
 * nor may Indiana University or Community Grids Lab or GlobalMMCS appear
 * in their name, without prior written permission of ARTI.
 *
 * Indiana University provides no reassurances that the source code provided
 * does not infringe the patent or any other intellectual property rights of
 * any other entity.  Indiana University disclaims any liability to any
 * recipient for claims brought by any other entity based on infringement of
 * intellectual property rights or otherwise.
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS.
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS",
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION
 *GENERATED USING SOFTWARE.
 */

import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;

import cgl.narada.transport.Link;
import cgl.narada.transport.LinkFactory;
import cgl.narada.transport.TransportDebugFlags;
import cgl.narada.transport.TransportException;
import cgl.narada.transport.TransportHandler;

/** An RTP implementation of the LinkFactory.
 * It can construct both unicast and multicast rtplinks.
 * It provides one method for each.
 *
 *  @author Shrideep Pallickara and Ahmet Uyar
*/
public class RTPLinkFactory implements LinkFactory, TransportDebugFlags {

   private String managedLinkType = "rtp";
   private String moduleName = "RTPLinkFactory: ";
   private Hashtable links;
   private TransportHandler transportHandler;
   private DatagramSocket rtpDataListenerSocket;
   private DatagramSocket rtpControlListenerSocket;

   private RTPLinkManager linkManager;
   private boolean linkManagerStarted = false;

   public RTPLinkFactory() {
      links = new Hashtable();

      try {
         linkManager = new RTPLinkManager(this);
      } catch (Exception e) {
         e.printStackTrace();
      }
      System.out.println("RTPLinkManager is initialized.");
      System.out.println("RTPLinkFactory is initialized.");
   }

   /**
    *  There is nothing to set.
    */
   public void setLinkFactoryProperties(Properties factoryProperties) {

   }

   /** Return the type of all the links created/managed by this factory */
   public String getManagedLinksType() {
      return managedLinkType;
   }

   /**
    * does nothing
   */
   public Link createLink(Properties linkProperties) throws TransportException {
      return null;
   }

   /**
    * create a linkId with the given ip address and port number
    * @param remoteAddress
    * @param localPort
    */
   public static String createLinkID(InetAddress remoteAddress, int localPort){
     return "rtp://" + remoteAddress.getHostName() + ":" + localPort;
   }

   /**
    * this method construct a unicast rtp link.
    *
    * @param remoteAddress the ip address of the remote party.
    * @param remotePort the port number of the remote client
    * @param localPort the udp port on the broker to receive packages from this client
    * @param publicationTopic the topic number to publish the received messages.
    * @return the initialized rtp link.
    * @throws Exception
    */
   public RTPLink createRTPLink(InetAddress remoteAddress,
                                int remotePort,
                                int localPort,
                                int publicationTopic)
         throws Exception {

      if( (localPort % 2) != 0  ){
         throw new Exception("The specified localPort is not an even number: "+localPort);
      }

      if( (remotePort % 2) != 0  ){
         throw new Exception("The specified remotePort is not an even number: "+remotePort);
      }

      if( (publicationTopic % 2) != 0  ){
         throw new Exception("The specified publicationTopic is not an even number: "+publicationTopic);
      }

      String key  = createLinkID(remoteAddress, localPort);

      if (links.containsKey(key)) {
         throw new Exception("Link already exists to: " + key);
      }

      RTPLink link = new RTPLink(remoteAddress, remotePort, localPort, publicationTopic, this);

      links.put(key, link);
      transportHandler.registerLink(this, link);

      /** Get a node address for this link */
      byte[] nodeAddressReq = new byte[1];
      nodeAddressReq[0] = cgl.narada.protocol.Protocol.NAP_CLIENT_ADD;
      transportHandler.dataReceived(nodeAddressReq, key);

      return link;
   }

   /**
    * construct a multicast rtp link. it will listen on a multicast address
    * and publish the received streams on the broker.
    * it can also sends streams from broker to the multicast address
    * by subscribing to topics.
    *
    * @param multicastAddress the multicast ip address
    * @param port multicast port number.
    * @param publicationTopic the base topic number to publish. firts stream will be published on this topics.
    * @param topicRange the maximum number of topics to use to publish messages.
    * @throws Exception
    */
   public MulticastRTPLink createMulticastRTPLink(InetAddress multicastAddress,
         int port,
         int publicationTopic,
         int topicRange)
         throws Exception {

      if( (port % 2) != 0  ){
         throw new Exception("The specified port is not an even number: "+port);
      }

      if( (publicationTopic % 2) != 0  ){
         throw new Exception("The specified publicationTopic is not an even number: "+publicationTopic);
      }

      String key = createLinkID(multicastAddress, port);

      if (links.containsKey(key)) {
         throw new Exception(moduleName + "Link already exists to: " + key);
      }

      MulticastRTPLink link = new MulticastRTPLink(multicastAddress, port, publicationTopic, topicRange, this);
      links.put(key, link);
      transportHandler.registerLink(this, link);

      /** Get a node address for this link */
      byte[] nodeAddressReq = new byte[1];
      nodeAddressReq[0] = cgl.narada.protocol.Protocol.NAP_CLIENT_ADD;
      transportHandler.dataReceived(nodeAddressReq, key);

      return link;

   }

   /**
    * close the link which has the given id
    */
   public void closeAllLinks() {

     Enumeration enumeration = links.keys();
     while(enumeration.hasMoreElements()){
       String linkID = (String)enumeration.nextElement();
       closeLink(linkID);
     }

   }

   /**
    * close the link which has the given id
    */
   public boolean closeLink(String linkID){

      //get this link
      Link link = (Link)links.get(linkID);
      if(link == null){
         return false;
      }

      transportHandler.closeLinks( link.getNaradaBrokeringId() );
      transportHandler.manageLinkLoss( link );

      //remove from the link list
      Object removedLink = links.remove(linkID);
      if(removedLink == null){
         System.out.println("Can not remove the link: "+linkID);
         return false;
      }

      //close this link
      System.out.println("Link: "+linkID+"  is closed.");
      return true;
   }

   /**
    * return the link with the given linkID
    */
   public Link getLink(String linkID){
      return (Link)links.get(linkID);
   }

   /** Set the interval for checking status of all links managed by this
       factory to the one specified in the argument */
   public void setLinkStatusInterval(long interval) {
   }

   /** Starts communication services. In this case what we do is start the
       RTPListener thread, which listens to datagram packets arriving at the
       RTPListenerPort */
   public boolean loadLinkFactoryServices() throws TransportException {
      return true;
   }

   /** Indicates if this factory can manage migration of links of a certain
       type */
   public boolean canManageLinkMigrationOfType(String linkType) {
      /**NBNI*/
      return false;
   }

   /** Used to migrate communications of a link to the one supported by
       links managed by this factory */
   public void manageLinkMigration(Link link) throws TransportException {
      /**NBNI*/
   }

   /** Sets the reference to the transport handler. This is done during the
       loadLinkFactory() operation within the transport handler
       implementations. */
   public void setTransportHandler(TransportHandler transportHandler) {
      this.transportHandler = transportHandler;
   }

   /** This method is used to garbage collect any resources associated with
       the link factory */
   public void dispose() {
      Enumeration _links = links.elements();

      while (_links.hasMoreElements()) {
         Link link = (Link) _links.nextElement();
         link.closeLink();
      }
   }

   public void handleInboundRTPLinkRequest(InetAddress remoteAddress, int remotePort) {
   }

   public void reportLinkLoss(Link linkLost) {
      String _linkId = linkLost.getLinkId();

      if (_linkId == null) {
         System.out.println(moduleName + "ReportLinkLoss Link Id is null");
      }

      if (links.containsKey(_linkId)) {
         links.remove(_linkId);

         if (RTPLinkFactory_Debug) {
            System.out.println(moduleName + "Managed Link Loss");
         }

         return;
      }

      System.out.println(moduleName + "Unaware of link [" + _linkId + "] whose connection loss is being reported");
   }

   public TransportHandler getTransportHandler() {
      if (transportHandler == null) {
         System.out.println(moduleName + "TransportHandler reference is NULL");
      }

      return transportHandler;
   }

}
